#include "GLHeaders.hpp"
#include "Vector.hpp"
#include "Bezier.hpp"

//////////////////////
//    Constructor   //
//////////////////////

Bezier::Bezier(void){
	v0 = Vector::ZERO;
	v1 = Vector::ZERO;
	v2 = Vector::ZERO;
	v3 = Vector::ZERO;
}

Bezier::Bezier(
		const Vector& vect0,
		const Vector& vect3){
	v0 = vect0;
	v1 = vect0;
	v2 = vect3;
	v3 = vect3;
}

Bezier::Bezier(
		const Vector& vect0,
		const Vector& vect1,
		const Vector& vect2,
		const Vector& vect3){
	v0 = vect0;
	v1 = vect1;
	v2 = vect2;
	v3 = vect3;
}

//////////////////////
//    Destructor    //
//////////////////////

Bezier::~Bezier(void){}

//////////////////////////////
//  Arithmetic operations   //
//////////////////////////////

Bezier Bezier::operator + () const{
  return Bezier(v0, v1, v2, v3);
}

Bezier Bezier::operator - () const{
  return Bezier(v3, v2, v1, v0);
}

Bezier Bezier::operator + (const Bezier   &rhs) const{
  return Bezier(v0+rhs.v0, v1+rhs.v1, v2+rhs.v2, v3+rhs.v3);
}

Bezier Bezier::operator + (const Vector &value) const{
  return Bezier(v0+value, v1+value, v2+value, v3+value);
}

Bezier Bezier::operator - (const Bezier   &rhs) const{
  return Bezier(v0-rhs.v0, v1-rhs.v1, v2-rhs.v2, v3-rhs.v3);
}

Bezier Bezier::operator - (const Vector &value) const{
  return Bezier(v0-value, v1-value, v2-value, v3-value);
}

Bezier Bezier::operator * (const GLfloat value) const{
  return Bezier(v0*value, v1*value, v2*value, v3*value);
}

Bezier Bezier::operator / (const GLfloat value) const{
  return Bezier(v0/value, v1/value, v2/value, v3*value);
}

//////////////////////////
//    Friend Methods    //
//////////////////////////

Bezier operator + (const Vector &value, const Bezier &rhs){
  return Bezier(value+rhs.v0, value+rhs.v1, value+rhs.v2, value+rhs.v3);
}

Bezier operator - (const Vector &value, const Bezier &rhs){
  return Bezier(value-rhs.v0, value-rhs.v1, value-rhs.v2, value-rhs.v3);
}

Bezier operator * (const GLfloat value, const Bezier &rhs){
  return Bezier(value*rhs.v0, value*rhs.v1, value*rhs.v2, value*rhs.v3);
}

Bezier operator / (const GLfloat value, const Bezier &rhs){
  return Bezier(value/rhs.v0, value/rhs.v1, value/rhs.v2, value/rhs.v3);
}

//////////////////////////////
//    Arithmetic updates    //
//////////////////////////////

Bezier& Bezier::operator  = (const Bezier   &rhs){
  this->v0 = rhs.v0;
  this->v1 = rhs.v1;
  this->v2 = rhs.v2;
  this->v3 = rhs.v3;

  return *this;
}

Bezier& Bezier::operator  = (const Vector &value){
  this->v0 = Vector::ZERO;
  this->v1 = Vector::ZERO;
  this->v2 = value;
  this->v3 = value;

  return *this;
}

Bezier& Bezier::operator += (const Bezier   &rhs){
  this->v0 += rhs.v0;
  this->v1 += rhs.v1;
  this->v2 += rhs.v2;
  this->v3 += rhs.v3;

  return *this;
}

Bezier& Bezier::operator += (const Vector &value){
  this->v0 += value;
  this->v1 += value;
  this->v2 += value;
  this->v3 += value;

  return *this;
}

Bezier& Bezier::operator -= (const Bezier   &rhs){
  this->v0 -= rhs.v0;
  this->v1 -= rhs.v1;
  this->v2 -= rhs.v2;
  this->v3 -= rhs.v3;

  return *this;
}

Bezier& Bezier::operator -= (const Vector &value){
  this->v0 -= value;
  this->v1 -= value;
  this->v2 -= value;
  this->v3 -= value;

  return *this;
}

Bezier& Bezier::operator *= (const GLfloat value){
  this->v0 *= value;
  this->v1 *= value;
  this->v2 *= value;
  this->v3 *= value;

  return *this;
}

Bezier& Bezier::operator /= (const GLfloat value){
  this->v0 /= value;
  this->v1 /= value;
  this->v2 /= value;
  this->v3 /= value;

  return *this;
}

//////////////////////
// Useful functions //
//////////////////////

Vector Bezier::Position(GLfloat time) const{

  if(0.0f > time || 1.0f < time)
    return Vector::ZERO;

  GLfloat t  = time;
  GLfloat t2 = t*t;
  GLfloat t3 = t2*t; 
  GLfloat f0 = 1 - 3*t + 3*t2 - t3;
  GLfloat f1 = 3*t - 6*t2 + 3*t3;
  GLfloat f2 = 3*t2 - 3*t3;
  GLfloat f3 = t3;

  return Vector(f0*v0 + f1*v1 + f2*v2 + f3*v3);
}

Vector Bezier::FirstDerivative(GLfloat time) const{

  if(0.0f > time || 1.0f < time)
    return Vector::ZERO;

  GLfloat t  = time;
  GLfloat t2 = t*time;
  GLfloat f0 = -3 + 6*t - 3*t2;
  GLfloat f1 = 3 - 12*t + 9*t2;
  GLfloat f2 = 6*t - 9*t2;
  GLfloat f3 = 3*t2;

  return Vector(f0*v0 + f1*v1 + f2*v2 + f3*v3);
}

Vector Bezier::SecondDerivative(GLfloat time) const{
 
  if(0.0f > time || 1.0f < time)
    return Vector::ZERO;

  GLfloat t  = time;
  GLfloat f0 = 6 - 6*t;
  GLfloat f1 = 12 + 18*t;
  GLfloat f2 = 6 - 18*t;
  GLfloat f3 = 6*t;

  return Vector(f0*v0 + f1*v1 + f2*v2 + f3*v3);
}

//////////////////////
//  Special Bezier  //
//////////////////////

const Bezier Bezier::ZERO(
    Vector::ZERO,
    Vector::ZERO,
    Vector::ZERO,
    Vector::ZERO);

const Bezier Bezier::UNIT_X(
    Vector::ZERO,
    Vector::ZERO,
    Vector::UNIT_X,
    Vector::UNIT_X);

const Bezier Bezier::UNIT_Y(
    Vector::ZERO,
    Vector::ZERO,
    Vector::UNIT_Y,
    Vector::UNIT_Y);

const Bezier Bezier::UNIT_Z(
    Vector::ZERO,
    Vector::ZERO,
    Vector::UNIT_Z,
    Vector::UNIT_Z);

const Bezier Bezier::NEGATIVE_UNIT_X(
    Vector::ZERO,
    Vector::ZERO,
    Vector::NEGATIVE_UNIT_X,
    Vector::NEGATIVE_UNIT_X);

const Bezier Bezier::NEGATIVE_UNIT_Y(
    Vector::ZERO,
    Vector::ZERO,
    Vector::NEGATIVE_UNIT_Y,
    Vector::NEGATIVE_UNIT_Y);

const Bezier Bezier::NEGATIVE_UNIT_Z(
    Vector::ZERO,
    Vector::ZERO,
    Vector::NEGATIVE_UNIT_Z,
    Vector::NEGATIVE_UNIT_Z);

